/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package websocket.chat;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import com.bean.User;
import com.dao.GroupDao;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

@ServerEndpoint(value = "/websocket/chat")
public class ChatAnnotation {

    private static final Log log = LogFactory.getLog(ChatAnnotation.class);


    private static final Set<ChatAnnotation> connections =
            new CopyOnWriteArraySet<>(); //websocket 连接管理集合

    public   String nickname=null; //昵称value为qq好

    private Session session; // session
    //构造方法
    public ChatAnnotation() {

    }

    /**
     * socket 启动打开
     * @param session
     */
    @OnOpen
    public void start(Session session) {
        this.session = session;
        connections.add(this);
    }

    /**
     * socket 关闭结束时调用
     */
    @OnClose
    public void end() {
        connections.remove(this);

        String message = String.format("%s",
                nickname+",has disconnected,:END");
        if (GloabStatus.mapUser.containsKey(this.nickname)){
            GloabStatus.mapUser.remove(this.nickname);
        }
        broadcast(message);

    }

    /**
     * 消息进来时调用
     * @param message
     */
    @OnMessage
    public void incoming(String message) {
        // Never trust the client
        String filteredMessage = message.toString();
        if(filteredMessage.endsWith(":REG")){ //注册
            //进行注册
           String[] array= filteredMessage.split(",");
            nickname= array[0];
            broadcast(filteredMessage);
            GloabStatus.mapUser.put(nickname,nickname);
            return;

        } else if(filteredMessage.endsWith(":STZ")){ //添加好友
            //进行单个好友通知
            String[] array= filteredMessage.split(",");
            String to= array[0];
            String sms= array[1];
            singleCast(to,filteredMessage);
            return;

        }else if(filteredMessage.endsWith(":SDE")){ //好友删除
            //进行单个好友通知
            String[] array= filteredMessage.split(",");
            String to= array[0];
            singleCast(to,filteredMessage);
            return;

        }else if(filteredMessage.endsWith(":GTZ")){ //添加群组好友
            //进行单个好友通知
            String[] array= filteredMessage.split(",");
            String to= array[0];
            String sms= array[1];
            groupCast(to,filteredMessage);
            return;

        }else if(filteredMessage.endsWith(":GQT")){ //群退出
            //进行单个好友通知
            String[] array= filteredMessage.split(",");
            String to= array[0];
            groupCast(to,filteredMessage);
            return;

        }else if(filteredMessage.endsWith("}") && filteredMessage.startsWith("{")){ // 消息通知
            String qq = getToQQ(filteredMessage);
            if(qq.length()==7){ //点对点消息
                singleCast(qq,filteredMessage);
            }else { //群消息
                groupCast(qq,filteredMessage);
            }
            return;
        }
        broadcast(filteredMessage);
    }


    /**
     * 发送错误时调用
     * @param t
     * @throws Throwable
     */
    @OnError
    public void onError(Throwable t) throws Throwable {
        log.error("Chat Error: " + t.toString(), t);
    }

    /**
     * 全部广播
     * @param msg
     */
    private  void broadcast(String msg) {
        for (ChatAnnotation client : connections) {
            try {
                synchronized (client) {

                    client.session.getBasicRemote().sendText(msg);
                }
            } catch (IOException e) {
                log.debug("发送失败异常", e);
                connections.remove(client);
                if (GloabStatus.mapUser.containsKey(client.nickname)){
                    GloabStatus.mapUser.remove(client.nickname);
                }

                try {
                    client.session.close();
                } catch (IOException e1) {

                }
                String message = String.format("* %s %s",
                        client.nickname, "has been disconnected,:END");
                log.info(message);
                broadcast(message);
            }
        }
    }

    /**
     * 单人发送
     * @param to
     * @param msg
     */
    private  void singleCast(String to, String msg) {
        for (ChatAnnotation client : connections) {
            try {
                synchronized (client) {
                    //点对点发送
                    if(client.nickname!=null && client.nickname.equals(to)){
                        client.session.getBasicRemote().sendText(msg);
                    }
                }
            } catch (IOException e) {
                log.debug("发送失败异常", e);
                connections.remove(client);
                if (GloabStatus.mapUser.containsKey(client.nickname)){
                    GloabStatus.mapUser.remove(client.nickname);
                }
                try {
                    client.session.close();
                } catch (IOException e1) {
                    log.debug("websocket异常", e);
                }
            }
        }
    }

    /**
     * 群组发送
     * @param to
     * @param msg
     */
    private  void groupCast(String to, String msg) {
        Map<String,String> map =getUserListByGroup(to);
        for (ChatAnnotation client : connections) {
            try {
                synchronized (client) {
                    //发送
                    if(client.nickname!=null && map.containsKey(client.nickname)){
                        if(client.nickname!= this.nickname){
                            client.session.getBasicRemote().sendText(msg);
                        }
                    }
                }
            } catch (IOException e) {
                log.debug("发送失败异常", e);
                connections.remove(client);
                if (GloabStatus.mapUser.containsKey(client.nickname)){
                    GloabStatus.mapUser.remove(client.nickname);
                }
                try {
                    client.session.close();
                } catch (IOException e1) {
                    log.debug("websocket异常", e);
                }
            }
        }
    }

    /**
     * 从消息中获取toQQ
     * @param jsonMessage
     * @return
     */
    public String getToQQ(String jsonMessage){
        //{"fromQQ":"2540187","message":"士大夫撒的","toQQ":"2540187","createTime":"2017-5-16 23:01:49"}
        String[] array = jsonMessage.split(",");
        String toQQ="";
        for (String s:array){
            if(s.indexOf("toQQ")>=0){
                toQQ= s.split(":")[1];
                break;
            }
        }
        return toQQ.replaceAll("\"","");
    }

    /**
     * 获取一个群主list的所以QQ号
     * @param groupQQ
     * @return
     */
    public static Map<String,String> getUserListByGroup(String groupQQ){

        GroupDao groupDao = new GroupDao();
        Map<String,String> map = new HashMap<>();
        List<User> list =  groupDao.getGroupOneByQQ(groupQQ).getUserList();
        if(list!=null && list.size()>0){
            for (User u:list){
                map.put(u.getQq(),u.getQq());
            }
        }
        return map;
    }

}
